#ifndef COMMON_H
#define COMMON_H
#include<sys/types.h>
#include <sys/stat.h>
#include<dirent.h>
#include  <stdio.h>
#include <string.h>
#include <libgen.h>
#include <time.h>
#include <fcntl.h>
#include<opencv2/opencv.hpp>
#include<jpmd5.h>
#include<globalfunc.h>
#include<jpmysqllib.h>

/**********************************************************************************
 * opencv make 错误 参考下列地址
 * https://github.com/Itseez/opencv/commit/ea50be0529c248961e1b66293f8a9e4b807294a6
 *********************************************************************************/
/*********************************************************************************
 * 通讯协议：
 * 1.客户端连接服务端，然后客户端发送操作指令，操作指令参考OP_*
 * 2.服务的接受对应操作指令，执行对应逻辑，当操作指令为OP_FILE表示接受一个文件，这时就用JPData数据格式作为传送数据的包格式(包的大小最好不要超过1500字节)，里面含有文件的重要信息和通讯信息
 * 3.执行完指令等待客户端再次发送指令。
 * *******************************************************************************/
using namespace cv;
#define UPLOAD_DIR "res/app/2014-12-11/"   //设置上传路径
#define SEARCH_DIR "/home/wjp/simg"   //设置搜索图片的根目录
#define OP_FILE 0x0011     //发送文件操作
#define OP_NUMBER 0x0022   //发送数字操作
#define OP_STRING 0x0033    //发送字符串操作
#define OP_NIL 0x0044    //空操作
#define OP_CLOSE 0x0055    //终止连接
clockid_t start_t,end_t,total_t; //程序执行时间
char *image_ext[]={"jpg","png","gif","bmp"};//读取图片的格式,后缀
pthread_mutex_t w_mutex = PTHREAD_MUTEX_INITIALIZER;//线程锁
typedef struct _Jp{
    unsigned int file_size;   //文件总大小
    unsigned int send_size;   //此次连接将要发送多少大小的字节数
    unsigned int sigl_block;  //单个块大小,一次tcp请求发送的字节大小
    unsigned int seek;        //从文件的那个位置开始
    unsigned int waveid;      //仓库id，每一次传输都有一个唯一id
    char filename[900];     //发送过来的文件名
}JpData;

/*********************读取接收数据大小size**************************/
/*******************************参数：socket,返回值：要接受的数据大小********************************/
int getpacksize(int sock){
unsigned int len=0;
int recvbt;
unsigned char rbuffer[16]={0};
recvbt=recv(sock,rbuffer,16,0);
if(recvbt>0){
    memcpy(&len,rbuffer,sizeof(unsigned int));
    len=ntohl(len);
}

return len;
}
/******************************读取一个字节内容**************************************/
/*******************************参数：socket,返回值：接受的字节值********************************/
unsigned char get_op(int sock){
int recvbt=0;
unsigned char rbuffer;
memset(&rbuffer,0,1);
recvbt=recv(sock,&rbuffer,1,0);
if(recvbt>0){
    return rbuffer;
}

return NULL;
}



/****************************************接受Jpdata数据包**************************************/
/***************************参数：socket,返回值:Jpdata数据包 ************************************/
JpData getJpData(int sock){
printf("开始接受JpData数据包:\n");
JpData jpdata;
char *buf;
buf=(char *)malloc(sizeof(jpdata));

int recvbt=recv(sock,buf,sizeof(jpdata),0);
if(recvbt>0){
memcpy(&jpdata,buf,sizeof(jpdata));
printf("接受JpData数据包:%d\n",recvbt);
}else{
   memset(&jpdata,0,sizeof(jpdata));
}
printf("接受JpData数据包,文件总字节数:%d\n",jpdata.file_size);
printf("接受JpData数据包,此次将要接受字节数:%d\n",jpdata.send_size);
printf("接受JpData数据包,单次发送字节数:%d\n",jpdata.sigl_block);
printf("接受JpData数据包,仓库id:%d\n",jpdata.waveid);
printf("接受JpData数据包,文件起始位置:%d\n",jpdata.seek);
printf("接受JpData数据包,文件名:%s\n",jpdata.filename);
free(buf);
return jpdata;

}





/***************************判断目录是否存在，不存在则创建*****************************************/
/***************************参数：目录 *********/
void create_dir(const char * dir){
    if(strlen(dir)==0 || dir==NULL){
        printf("路径为空\n");
        return ;
    }
    DIR *F=opendir(dir);
    if(mkdir(dir,0777)==-1&&F==NULL){
        printf("准备创建目录");
        //判断父目录存在否存在直接创建目录，否则递归调用创建目录
        char tmp[2024]={0};
        char *p;
        int len=strlen(dir);
        memset(tmp,0,sizeof(tmp));
        strncpy(tmp,dir,len);
        if(tmp[len-1]=='/'){
            tmp[len-1]='\0';
        }
        p=rindex(tmp,'/');
        if(p){
            *p='\0';
        }
        create_dir(tmp);
        DIR *F2=opendir(dir);
        if(F2==NULL)
        mkdir(dir,0777);
        else{
        closedir(F2);
        }


    }else
    {
        closedir(F);
        printf("创建目录成功\n");
        return ;
    }

}

/***************************获取文件扩展名*****************************************/
/***************************参数：文件名称字符串,返回值：扩展名***********************/
char *get_file_ext(char *filename){
    char *p;
    p=rindex(filename,'.');
    if(p){
    return p+1;
    }else{
        return NULL;
    }

}

/***************************讲字符转化成小写*****************************************/
/***************************参数：字符串，返回：转化成小写字符***********************/
char *tolowerstr(char *str){
    char *rstr;
    int len=strlen(str);
    rstr=(char *)malloc(len+1);
    memset(rstr,0,len+1);
    memcpy(rstr,str,len);
    for(int i=0;i<len;i++){
        rstr[i]=tolower(rstr[i]);
    }
    return rstr;

}






/***************************接收数据包存入某个文件*****************************************/
/***************************参数：socket,参数：filename 文件名,参数：接受数据的总字节数*********/
void save_to_file(int sock,char *filename,JpData jpdata){
    if(sock>0){
        char *dir=dir_name(filename);
        if(dir!=NULL){
            create_dir(dir);
        }
        int size_block=jpdata.sigl_block;
        printf("block_size:%d\n",size_block);
        int block=0;



        while(1){
                block=getpacksize(sock);



                if(block<0){
                    break;
                }

                if(block>jpdata.send_size){
                    break;
                }
                char *buf;
                int recvbt;
                buf=(char *)malloc(size_block);
                memset(buf,0,size_block);
                recvbt=recv(sock,buf,size_block,0);
                pthread_mutex_lock(&w_mutex);
                FILE *f=fopen(filename,"r+b");
                //fseek(f,0,SEEK_SET);
                /*
                if(f==NULL){
                    printf("打开文件失败!\n");
                }else{
                    printf("打开文件成功\n");
                }
                */
                long int precur=ftell(f);
                fseek(f,jpdata.seek+block,SEEK_SET);
                fwrite(buf,sizeof(char),recvbt,f);
                long int cur=ftell(f);
                fclose(f);
                pthread_mutex_unlock(&w_mutex);
                free(buf);
                //printf("block:%d,写入位置从:%d,写到:%d,seek前指针位置:%ld,seek后指针位置:%ld\n",block,jpdata.seek+block,jpdata.seek+block+recvbt,precur,cur);
                //printf("has already recv:%d,start recv data len:%d,fisrt:0x%02x\n",block,recvbt,buf[0]);



        }

         free(dir);
         printf("close file \n");

    }

}

/****************************接受一串字符串，从socket*******************************/
/***************************参数：socket,返回值：接收到的字符串*******************/
char *recvstring(int sock){
    char *p;
    p=(char *)malloc(2048);
    memset(p,0,sizeof(2048));
    int recvbt=recv(sock,p,2048,0);
    if(recvbt>0){
        return p;
    }else{
        return NULL;
    }
}

unsigned long get_file_size(const char *filename){
    unsigned long size=0;
    FILE *fp=fopen(filename,"rb");
    if(fp!=NULL){
        fseek(fp,SEEK_SET,SEEK_END);
        size=ftell(fp);
        fclose(fp);
        return size;
    }else{
        return 0;
    }

}
Mat jisuanzhifangtu(Mat mat){
 Mat hist;
 cvtColor(mat,mat,CV_BGR2HSV);
 int channel[]={0,1};
 int hist_size[]={30,32};
 float hrange[]={0,180};
 float srange[]={0,256};
 const float *range[]={hrange,srange};
 calcHist(&mat,1,channel,Mat(),hist,2,hist_size,range,true,false);
 return hist;

}






/*****************************OPENCV直方图比较****************************/
/**********************************参数:1:图片1 Mat,2:图片2 Mat*********************/
void openCvHistCompt(Mat mat1,Mat mat2,double *histresult){

    cvtColor(mat1,mat1,COLOR_BGR2HSV);
    cvtColor(mat2,mat2,COLOR_BGR2HSV);
    int hsize=50,ssize=60;
    int histsize[]={hsize,ssize};
    int channels[] = { 0, 1 };
    float h_ranges[] = { 0, 180 };
    float s_ranges[] = { 0, 256 };

    const float* ranges[] = { h_ranges, s_ranges };
    MatND hist_base1,hist_base2;
    calcHist( &mat1, 1, channels, Mat(), hist_base1, 2, histsize, ranges, true, false );
    normalize(hist_base1,hist_base1,0,1,NORM_MINMAX,-1,Mat());
    calcHist( &mat2, 1, channels, Mat(), hist_base2, 2, histsize, ranges, true, false );
    normalize(hist_base2,hist_base2,0,1,NORM_MINMAX,-1,Mat());
    for( int i = 0; i < 4; i++ )
    {
         histresult[i]= compareHist( hist_base1, hist_base2, i);

    }


}


/*************************************处理两张图片逻辑******************************/
/**********************************参数:1:图片1路径,2:图片2路径*********************/
void processingImageLogic(char *image1,char *image2){
  Mat mat1,mat2;
  mat1=imread(image1);
  mat2=imread(image2);
  if(!is_hist(image2,image1)){
      //直方图比较
      //printf("比较%s和%s:\n",basename(image1),basename(image2));
      double hist[4];
      openCvHistCompt(mat1,mat2,hist);
      hist_to_db(image2,image1,hist);
  }else{
      //printf("比较已经存在\n");
  }





}





/***********************************递归搜索图片***********************************/
/**********************************参数:1:要搜索的根目录,2:要比较的图片路径******************************/
void bianlimulu(char *_dirname,char *src_img_path){
    DIR *dir=opendir(_dirname);
    struct dirent *entry;
    if(dir!=NULL){
        while((entry=readdir(dir))!=NULL){
            if(entry->d_type&DT_DIR){
                    if(strcmp(".",entry->d_name)==0||strcmp("..",entry->d_name)==0)
                        continue;
                    int slen=strlen(entry->d_name)+strlen(_dirname)+2;
                    char *newdir=(char *)malloc(slen);
                    memset(newdir,0,slen);
                    sprintf(newdir,"%s/%s",_dirname,entry->d_name);
                    //printf("新目录:%s\n",newdir);
                    bianlimulu(newdir,src_img_path);
                    free(newdir);
            }else{
                char *ext=get_file_ext(entry->d_name);
                if(ext!=NULL){

                    char *lowerext=tolowerstr(ext);
                    //判断图片后缀是否是系统设置的
                    if(check_ext(lowerext,image_ext,sizeof(image_ext)/sizeof(image_ext[0]))){
                        //格式符合
                        int slen2=strlen(entry->d_name)+strlen(_dirname)+100;
                        char *fullname=(char *)malloc(slen2);

                        memset(fullname,0,slen2);

                        sprintf(fullname,"%s/%s",_dirname,entry->d_name);

                        check_image_path(fullname,0,false);
                        //printf("完整文件路径:%s\n",arg);
                        processingImageLogic(fullname,src_img_path);

                        free(fullname);

                       // printf("%s,%s\n",fullname,src_img_path);
                    }
                    free(lowerext);



                    //printf("扩展名:%s,%s\n",ext,lowerext);
                }else{
                   // printf("文件:%s没有扩展名\n",entry->d_name);
                }

            }

        }
    }else{
        printf("打开目录失败\n");
    }
}

/**************************测试函数，返回1测试用，返回0关闭测试******************************/
int test(){

    //bianlimulu(SEARCH_DIR,"/home/wjp/w_app/server/build-server-Desktop-Release/a.jpg");
    // jpmysql_open();
    //int res= mysql_query(con,"insert into jdx_image_info(path,hist1,hist2,hist3,hist4,codeno,filename) values('/var/www/a.jpg',2.54,6.36,69.25,70.55,'PESASDF','中国')");
    //int res=jp_insert("jdx_image_info","path,codeno,filename","'/var/www/PE525895_2.jpg','PESASDF','中国'");
    //printf("返回值：%d\n",res);
    //int res=jp_delete("jdx_image_info",NULL);
    //printf("返回值：%d\n",res);


    //hist_to_db("res/app/2014-12-11/LSWC0170BA_1.jpg","/home/wjp/simg/LSWC0170CS_1.jpg",NULL);

    return 0;
}


/*********************opencv处理逻辑************************/
void opencvLogic(char *filename,JpData jpdata,unsigned long size){
    //unsigned long size=get_file_size(filename);
    //Mat mat=imread(filename,1);
    //*hassave=jpdata.send_size+(*hassave);
    if(size==jpdata.file_size){
        //Mat mat=imread(filename,1);
        //imshow(jpdata.filename,mat);
        //chulituxiang(mat);
        check_image_path(filename,1,false);
        bianlimulu(SEARCH_DIR,filename);
        get_top_20(filename,"order by comp.hist1 desc");
       // printf("起始位置:%d,读取多少:%d,文件接受完毕\n",jpdata.seek, size);
       // waitKey(0);
    }
}
/***************************处理接收文件逻辑**************************************/
/***************************参数：socket****************************************/
void recv_file_logic(int acc_sock){
    char full_path[2048]={0};

    JpData jpdata=getJpData(acc_sock);

    if(jpdata.send_size>0){
        strcat(full_path,UPLOAD_DIR);

        strcat(full_path,jpdata.filename);
        int flag=remove(full_path);
        FILE *f=fopen(full_path,"r");
        if(f==NULL){
            int fd = open(full_path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
            lseek(fd, jpdata.file_size - 1, SEEK_SET);
            write(fd, "0", 1);
            close(fd);
        }else{
          fclose(f);
        }

        if(flag==-1){
            printf("删除文件失败\n");
        }else{
            printf("删除文件成功\n");
        }

        save_to_file(acc_sock,full_path,jpdata);
        /*
        Mat mat=imread(full_path,1);
        if(mat.rows>0&&mat.cols>0){


        }
        */
        //读取已经发送过来的数据包大小
        unsigned long size=getpacksize(acc_sock);

        opencvLogic(full_path,jpdata,size);
       // printf("保存完毕:%d\n",mat.rows);



    }else{

    printf("没有接收到jdata数据包\n");

    }
}


/***************************处理接收字符串**************************************/
/***************************参数：socket****************************************/
void recv_string_logic(int acc_sock){
   printf("处理接收字符串逻辑\n");
}

/***************************处理接收数值**************************************/
/***************************参数：socket****************************************/
void recv_number_logic(int acc_sock){
   printf("处理接收数值逻辑\n");
}

/***************************接收数据包逻辑,发送数据前，先发送数据总字节大小，再发送传输过来即将保存的文件名*****************************************/
/**********************************************参数：socket,返回值，继续执行返回１，终止退出返回-1*******************************************/
void recvLogic(int acc_sock){

 while(1){

     //接受指令，看是什么操作,操作参考OP_*;

     uchar i=get_op(acc_sock);

     switch(i){
     case OP_FILE:
         printf("接受到指令:0x%02x,处理传输文件\n",i);
         recv_file_logic(acc_sock);
         printf("处理传输文件完毕\n");
         break;
     case OP_STRING:
         recv_string_logic(acc_sock);
         break;
     case OP_NUMBER:
         recv_number_logic(acc_sock);
         break;
     case OP_NIL:
         continue;
         break;
     case OP_CLOSE:
         close(acc_sock);
         printf("客户端(主动)断开连接:%d\n",acc_sock);
         return ;
         break;
     case 0:
         close(acc_sock);
         printf("客户端断开连接(被动)关闭socket:%d\n",acc_sock);
         return ;
         break;
     }









 }


}

#endif // COMMON_H
